import { factory } from "../utils/html";
import mono_hlsb from "../utils/mono_hmsb_in_worker";
import { ComponentClass } from "./ComponentClass";
const canvas = factory('canvas');
const cssPixelated ='image-rendering: optimizeSpeed;' + // FireFox < 6.0
                    'image-rendering: -moz-crisp-edges;' + // FireFox
                    'image-rendering: -o-crisp-edges;' +  // Opera
                    'image-rendering: -webkit-crisp-edges;' + // Chrome
                    'image-rendering: crisp-edges;' + // Chrome
                    'image-rendering: -webkit-optimize-contrast;' + // Safari
                    'image-rendering: pixelated; ' + // Future browsers
                    '-ms-interpolation-mode: nearest-neighbor;'; // IE
export class TileEditor extends ComponentClass{
  /**
   * 
   * @param {Uint8Array} tileImageData 
   * @param {number} blockWidth 
   * @param {number} blockHeight 
   * @param {number} scale 
   * @param {[number,number,number]} color 
   * @param {(x:number, y:number, tileEditor:TileEditor, event:MouseEvent)=>void} onPixelChange 
   */
  constructor(
    tileImageData,
    blockWidth=8,
    blockHeight=8,
    scale=2,
    color=[0,0,0],
    onPixelChange=undefined
  ){
    super();
    let cv = canvas({
      width: blockWidth * scale, //border 1px, head
      height: blockHeight * scale, //border 1px, head
      style: {cssText:cssPixelated}
    });
    this.element = cv;
    this.blockWidth = blockWidth;
    this.blockHeight = blockHeight;
    this.color = color;
    this.buffer = tileImageData;
    this.scale = scale;
    this.onPixelChange = onPixelChange;
    this.lastHoverGrid = {x:-1, y:-1};
    // render
    this.render();
    cv.onmousedown = this.__handleOnClick.bind(this);
    cv.onmousemove = this.__handleOnMove.bind(this);
    cv.onmouseleave = this.__handleOnLeave.bind(this);
    cv.addEventListener("contextmenu",event=>event.preventDefault());
  }
  valueOf(){
    return this.element;
  }

  // render function below
  async render(){
    /** @type {CanvasRenderingContext2D} */
    let ctx = this.element.getContext("2d", { willReadFrequently: true });
    let imgData = ctx.createImageData(this.element.width, this.element.height);
    imgData.data.set(await mono_hlsb.drawCanvasDataInWorker(this.buffer, imgData.width, imgData.height, this.scale, this.color));
    ctx.putImageData(imgData, 0, 0);
  }
  async renderPixel(x, y){
    let [index, bit] = this.__getBytePosition(x, y);
    let ctx = this.element.getContext("2d", { willReadFrequently: true });
    let imgData = ctx.createImageData(8*this.scale, this.scale);
    imgData.data.set(await mono_hlsb.drawCanvasDataInWorker(this.buffer.slice(index,index+1), imgData.width, imgData.height, this.scale, this.color));
    ctx.putImageData(imgData, (x-bit)*this.scale, y*this.scale);
  }
  __getBytePosition(x, y){
    let cols = Math.floor(this.blockWidth / 8);
    let col = Math.floor(x / 8);
    let bit = x % 8;
    return [y*cols + col, bit];
  }

  // event handler below
  __getMousePositionFromEvent(event){
    // convert to canvas axis
    let x = this.element.width*event.offsetX/this.element.clientWidth;
    let y = this.element.height*event.offsetY/this.element.clientHeight;
    let gridWidth = this.scale;
    let gridHeight = this.scale;
    let col = Math.floor(x / gridWidth);
    let row = Math.floor(y / gridHeight);
    return {x: col, y: row};
  }
  __handleOnClick(event){
    // convert to canvas axis
    let {x, y} = this.__getMousePositionFromEvent(event);
    if (x < 0 || y < 0 || x >= this.blockWidth || y >= this.blockHeight){
      return;
    }
    if (typeof(this.onPixelChange) == "function"){
      this.lastHoverGrid = {x, y};
      this.onPixelChange(x, y, this, event);
    }
  }
  __handleOnMove(event){
    let {x, y} = this.__getMousePositionFromEvent(event);
    if (x < 0 || y < 0 || x >= this.blockWidth || y >= this.blockHeight){
      return;
    }
    if (x == this.lastHoverGrid.x && y == this.lastHoverGrid.y){
      return; // not trigger when in same grid
    }
    if (event.buttons !== 1 && event.buttons !== 2){
      return;
    }
    if (typeof(this.onPixelChange) == "function"){
      this.lastHoverGrid = {x, y};
      this.onPixelChange(x, y, this, event);
    }
  }
  __handleOnLeave(event){
    this.lastHoverGrid = {x:-1, y:-1};
  }

  // public function
  setPixel(x, y, color){
    let [index, bit] = this.__getBytePosition(x, y);
    if (index > this.buffer.length){
      return;
    }
    let byt = this.buffer[index];
    let pat = 0x80 >> bit;
    if (color >= 1){
      byt = byt | pat;
    } else {
      byt = byt & (0xFF ^ pat);
    }
    this.buffer[index] = byt;
    this.renderPixel(x, y);
  }
  getPixel(x, y){
    let [index, bit] = this.__getBytePosition(x, y);
    if (index > this.buffer.length){
      return 0;
    }
    let byt = this.buffer[index];
    let r = byt & (0x80 >> bit);
    return r > 0 ? 1 : 0;
  }
} 